﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.IO;
using V8.Net;

namespace DreamSpace.Net.ScriptEngines.V8
{
    // ========================================================================================================================

    /// <summary>
    /// A global object wrapper for each individual V8-based server host instance.
    /// A wrapped '_Globals' object is set to '{DreamSpaceServer}.Globals' when '{V8ScriptEngine}.CreateSharedGlobalObject()' is called.
    /// </summary>
    public class Globals : V8ManagedObject
    {
        internal IDictionary<string, object> _Globals;

        public override InternalHandle NamedPropertyGetter(ref string propertyName)
        {
            // ... get the global property and update the local instance copy of it, then return the local value ...
            object value;
            lock (_Globals)
            {
                if (!_Globals.TryGetValue(propertyName, out value))
                    value = InternalHandle.Empty;
            }
            base.NamedPropertySetter(ref propertyName, Engine.CreateValue(value), V8PropertyAttributes.None);
            return base.NamedPropertyGetter(ref propertyName);
        }

        public override InternalHandle NamedPropertySetter(ref string propertyName, InternalHandle value, V8PropertyAttributes attributes = V8PropertyAttributes.Undefined)
        {
            // ... set the local value, then update the global property ...
            var result = base.NamedPropertySetter(ref propertyName, value, attributes);
            lock (_Globals)
            {
                _Globals[propertyName] = value.Value;
            }
            return result;
        }
    }

    /// <summary>
    /// This is the default scripting engine implementation supported by the DS.NET Server.
    /// </summary>
    public class V8ScriptEngine : IScriptEngine, IDisposable
    {
        // --------------------------------------------------------------------------------------------------------------------

        V8Engine _V8Engine;

        // --------------------------------------------------------------------------------------------------------------------

        public V8ScriptEngine()
            : this(false)
        {
        }

        public V8ScriptEngine(bool debugMode, int? debugPort = null)
        {
            var debugging = debugMode || debugPort != null;

            _V8Engine = new V8Engine();
        }

        public void Dispose()
        {
        }

        // --------------------------------------------------------------------------------------------------------------------

        public bool DefineGlobalProperty(string inScriptPropertyName, object objectInstance, ScriptMemberSecurity accessType)
        {
            return _V8Engine.GlobalObject.SetProperty(inScriptPropertyName, objectInstance, null, true, accessType);
        }

        // --------------------------------------------------------------------------------------------------------------------

        public bool DefineGlobalType(Type type, string inScriptTypeName = null, ScriptMemberSecurity accessType = ScriptMemberSecurity.Locked)
        {
            return _V8Engine.GlobalObject.SetProperty(type, V8PropertyAttributes.Locked, inScriptTypeName, true, accessType);
        }

        public bool DefineGlobalType<T>(string inScriptTypeName = null, ScriptMemberSecurity accessType = ScriptMemberSecurity.Locked)
        {
            return _V8Engine.GlobalObject.SetProperty(typeof(T), V8PropertyAttributes.Locked, inScriptTypeName, true, accessType);
        }

        // --------------------------------------------------------------------------------------------------------------------

        public IBasicHandle CreateSharedGlobalObject(IDictionary<string, object> globalObject)
        {
            var obj = _V8Engine.CreateObjectTemplate().CreateObject<Globals>();
            obj._Globals = globalObject;
            return obj.Handle;
        }

        // --------------------------------------------------------------------------------------------------------------------

        public void SetTimeoutCallback(Action<IScriptEngine> callBack)
        {
            throw new NotImplementedException();
        }

        public IBasicHandle Execute(string script, string sourceName = null, bool throwOnError = false)
        {
            return _V8Engine.Execute(script, sourceName, throwOnError);
        }

        public IBasicHandle Evaluate(string script, string sourceName = null, bool throwOnError = false)
        {
            return _V8Engine.Execute(script, sourceName, throwOnError);
        }

        public void Interrupt()
        {
            throw new NotImplementedException();
        }

        public IBasicHandle LoadScript(string scriptFile, string sourceName = "DreamSpace Server", bool throwExceptionOnError = false)
        {
            return _V8Engine.LoadScript(scriptFile, sourceName, throwExceptionOnError);
        }


        // --------------------------------------------------------------------------------------------------------------------
    }
}
